package com.googlecode.horncomb.orm.tool.codegen.support.ibatis;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.AccessType;
import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.Attributes;
import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.Basic;
import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.Column;
import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.Entity;
import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.EntityMappingsDocument;
import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.Id;
import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.Table;
import com.googlecode.horncomb.orm.jpa.v10.xmlbeans.EntityMappingsDocument.EntityMappings;

/**
 * @author Daniel
 */
@SuppressWarnings("unchecked")
public class SqlMapBuilder {
	// private Log logger = LogFactory.getLog(SqlMapBuilder.class);
	ArrayList fields = new ArrayList<AccessibleObject>();
	ArrayList methods = new ArrayList<AccessibleObject>();

	public SqlMapBuilder() {
	}

	public EntityMappingsDocument buildSqlMap(Class clz) throws Exception {
		// 是实体映射
		if (clz == null
				|| !clz.isAnnotationPresent(javax.persistence.Entity.class))
			return null;
		EntityMappingsDocument docu = EntityMappingsDocument.Factory
				.newInstance();
		EntityMappings ems = docu.addNewEntityMappings();
		ems.setPackage(clz.getPackage().getName());
		Entity entity = ems.addNewEntity();
		entity.setClass1(clz.getSimpleName());
		if (buildSuperclass(entity, clz, 0)) {
			entity.setAccess(AccessType.FIELD);
		} else {
			entity.setAccess(AccessType.PROPERTY);
		}
		return docu;
	}

	private boolean buildSuperclass(Entity entity, Class clz, int recuLvl)
			throws Exception {
		Class superClz = clz.getSuperclass();
		boolean useFieldMode = false;
		boolean usePropertyMode = false;
		ArrayList<Member> members = null;
		// 递归调用
		if (superClz
				.isAnnotationPresent(javax.persistence.MappedSuperclass.class)) {
			if (buildSuperclass(entity, superClz, recuLvl + 1)) {
				useFieldMode = true;
			} else {
				usePropertyMode = true;
			}
		}

		// Entity entityInfo = (Entity)clz.getAnnotation(Entity.class);
		// entityInfo.name();
		if (clz.isAnnotationPresent(javax.persistence.Table.class)) {
			Table tabBean = entity.addNewTable();
			javax.persistence.Table tab = (javax.persistence.Table) clz
					.getAnnotation(javax.persistence.Table.class);
			tabBean.setName(tab.name());
		}

		// 是否修改递归上一层定义的模式
		if (preparedMember(fields, clz.getDeclaredFields())) {
			useFieldMode = true;
		}
		if (preparedMember(methods, clz.getDeclaredMethods())) {
			usePropertyMode = true;
		}

		// 没有递归的，调用处理构造XML
		if (recuLvl == 0) {
			if ((useFieldMode && !usePropertyMode)
					|| (!useFieldMode && usePropertyMode)) {
				if (useFieldMode) {
					members = fields;
				} else if (usePropertyMode) {
					members = methods;
				}
			} else {
				throw new Exception(
						"both of useFieldMode and usePropertyMode are not true!");
			}
			this.processMember(entity, members);
		}

		// 是否使用FIELD模式
		return useFieldMode;
	}

	/**
	 * 预处理Member，判断使用Field模式还是Properties模式
	 * @param <T>
	 * @param members
	 * @param memAry
	 * @return
	 */
	public <T extends AccessibleObject> boolean preparedMember(List<T> members,
			T[] memAry) {
		boolean mode = false;
		for (T m : memAry) {
			// 如果是方法，必须是 get 或者 is 方法，且不处理serialVersionUID
			if (m instanceof Method
					&& !((Method) m).getName().startsWith("get")
					&& !((Method) m).getName().startsWith("is")
					|| StringUtils.equals(((Member) m).getName(),
							"serialVersionUID")) {
				continue;
			}
			// 标注Transient的类型不处理
			if (!m.isAnnotationPresent(javax.persistence.Transient.class)) {
				members.add(m);
				if (m.isAnnotationPresent(javax.persistence.Id.class)) {
					mode = true;
				}
			}
		}
		return mode;
	}

	/**
	 * 处理Member填充XML结构
	 * @param <T>
	 * @param entity
	 * @param members
	 */
	@SuppressWarnings("deprecation")
	public <T extends Member> void processMember(Entity entity, List<T> members) {
		if (members.size() == 0)
			return;
		Attributes attrsBean = entity.addNewAttributes();
		for (T m : members) {
			Id idBean = null;
			Basic bscBean = null;
			if (((AccessibleObject) m)
					.isAnnotationPresent(javax.persistence.Id.class)) {
				idBean = attrsBean.addNewId();
				idBean.setName(getMemberName(m));
			} else {
				bscBean = attrsBean.addNewBasic();
				bscBean.setName(getMemberName(m));
			}
			Column colBean = null;
			if (((AccessibleObject) m)
					.isAnnotationPresent(javax.persistence.Column.class)) {
				if (idBean != null) {
					colBean = idBean.addNewColumn();
				} else if (bscBean != null) {
					colBean = bscBean.addNewColumn();
				}
				javax.persistence.Column col = ((AccessibleObject) m)
						.getAnnotation(javax.persistence.Column.class);
				colBean.setName(col.name());
			} else {
				attrsBean.removeBasic(attrsBean.getBasicArray().length - 1);
			}
		}
	}

	/**
	 * 根据Member获取Name
	 * @param m
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public String getMemberName(Member m) {
		if (m instanceof Method) {
			// is打头的文件
			if (m.getName().startsWith("is")) {
				return StringUtils.uncapitalise(m.getName().substring(2));
			} else {
				return StringUtils.uncapitalise(m.getName().substring(3));
			}
		} else {
			return m.getName();
		}
	}
}
